//--------------------------------------------------------------------------
// $Id: KeyValuePairs.java 11 2006-08-25 19:19:49Z zendragon $
//--------------------------------------------------------------------------
// Copyright 2006 Social Brain Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//--------------------------------------------------------------------------

package org.socialbrain.openid;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;

import java.io.Serializable;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * @author <a href="http://erning.net/">Zhang Erning</a>
 * @version $Id: KeyValuePairs.java 11 2006-08-25 19:19:49Z zendragon $
 */
public class KeyValuePairs implements Serializable
{
    public KeyValuePairs()
    {
        _map = createMap();
    }

    public KeyValuePairs(KeyValuePairs pairs)
    {
        _map = pairs._map;
    }

    protected Map<String, String> createMap()
    {
        return new HashMap<String, String>();
    }

    @Override
    public boolean equals(Object o)
    {
        return o != null && (o instanceof KeyValuePairs) && _map.equals(((KeyValuePairs)o)._map);
    }

    @Override
    public int hashCode()
    {
        return _map.hashCode();
    }

    @Override
    public String toString()
    {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry entry : _map.entrySet())
        {
            sb.append(entry.getKey()).append('=').append(entry.getValue()).append(';');
        }
        return sb.toString();
    }

    //

    public String toQueryString()
    {
        return toQueryString(Constants.DEFAULT_CHARSET);
    }

    public String toQueryString(String encoding)
    {
        StringBuffer sb = new StringBuffer();
        Iterator<String> i = _map.keySet().iterator();
        try
        {
            if (i.hasNext())
            {
                String key = i.next();
                String value = _map.get(key);
                sb.append(Constants.PARAMETER_PREFIX).append(key)
                        .append('=')
                        .append(URLEncoder.encode(value, encoding));
            }
            while (i.hasNext())
            {
                String key = i.next();
                String value = _map.get(key);
                sb.append('&');
                sb.append(Constants.PARAMETER_PREFIX)
                        .append(key).append('=')
                        .append(URLEncoder.encode(value, encoding));
            }
        }
        catch (Exception e)
        {
            throw new OpenIdException("Unable to transform to query string", e);
        }
        return sb.toString();
    }

    public static KeyValuePairs parseQueryString(String s, String encoding)
    {
        if (encoding == null)
        {
            encoding = Constants.DEFAULT_CHARSET;
        }
        
        KeyValuePairs pairs = new KeyValuePairs();
        String[] lines = StringUtils.split(s, '&');
        try
        {
            for (String line : lines)
            {
                String[] kv = StringUtils.split(line, '=');
                String key = kv[0];
                if (key.startsWith(Constants.PARAMETER_PREFIX))
                {
                    pairs.put(key.substring(Constants.PARAMETER_PREFIX_LENGTH), URLDecoder.decode(kv[1], encoding));
                }
            }
        }
        catch (Exception e)
        {
            throw new OpenIdException("Unable to parse from query string", e);
        }
        return pairs;
    }

    public String toFormString()
    {
        StringBuffer sb = new StringBuffer();
        for (String key : _map.keySet())
        {
            String value = _map.get(key);
            sb.append(key).append(':').append(value).append('\n');
        }
        return sb.toString();
    }

    public static KeyValuePairs parseFormString(String s)
    {
        KeyValuePairs pairs = new KeyValuePairs();
        String[] lines = StringUtils.split(s, '\n');
        for (String line : lines)
        {
            try
            {
                String[] kv = line.split(":", 2);
                pairs.put(kv[0], kv[1]);
            }
            catch (Exception e)
            {
                throw new OpenIdException("Unable to parse from key-value form", e);
            }
        }
        return pairs;
    }

    public static KeyValuePairs parseParameters(Map<String, String> parameters)
    {
        KeyValuePairs pairs = new KeyValuePairs();
        for (Map.Entry<String, String> entry : parameters.entrySet())
        {
            if (entry.getKey().startsWith(Constants.PARAMETER_PREFIX))
            {
                pairs.put(entry.getKey().substring(Constants.PARAMETER_PREFIX_LENGTH), entry.getValue());
            }
        }
        return pairs;
    }

    //

    public void put(String key, String value)
    {
        if (value == null)
        {
            _map.remove(key);
        }
        else
        {
            _map.put(key, value);
        }
    }

    public String get(String key)
    {
        return _map.get(key);
    }

    public void remove(String key)
    {
        _map.remove(key);
    }

    public boolean contains(String key)
    {
        return _map.containsKey(key);
    }

    public Set<String> keySet()
    {
        return _map.keySet();
    }

    //

    public void putAsByteArray(String key, byte[] value)
    {
        if (value == null)
        {
            remove(key);
        }
        else
        {
            put(key, new String(Base64.encodeBase64(value)));
        }
    }

    public byte[] getAsByteArray(String key)
    {
        String value = get(key);
        if (value == null) return null;
        return Base64.decodeBase64(value.getBytes());
    }

    public void putAsBigInteger(String key, BigInteger value)
    {
        if (value == null)
        {
            remove(key);
        }
        else
        {
            putAsByteArray(key, value.toByteArray());
        }
    }

    public BigInteger getAsBigInteger(String key)
    {
        byte[] value = getAsByteArray(key);
        if (value == null) return null;
        return new BigInteger(value);
    }

    public void putAsInt(String key, Integer value)
    {
        if (value == null)
        {
            remove(key);
        }
        else
        {
            put(key, Integer.toString(value));
        }
    }

    public Integer getAsInt(String key)
    {
        String value = get(key);
        if (value == null) return null;
        try
        {
            return Integer.parseInt(value);
        }
        catch (Exception e)
        {
            return null;
        }
    }

    public void putAsBoolean(String key, Boolean value)
    {
        if (value == null)
        {
            remove(key);
        }
        else
        {
            put(key, value ? "true" : "false");
        }
    }

    public Boolean getAsBoolean(String key)
    {
        String value = get(key);
        if (value == null) return null;
        try
        {
            return Boolean.parseBoolean(value);
        }
        catch (Exception e)
        {
            return null;
        }
    }

    private Map<String, String> _map;
}
